//
//  Memory_BusSlave_Interface.cpp
//  sad
//
//  Created by Christopher Kemsley on 2/25/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "SystemCDefinitions.hpp"

#include "MemoryInterface.hpp"
#include "BusInterface.hpp"

#include "Settings.hpp"
#include "Codec.hpp"
#include "ModuleFactory.hpp"
#include "PubSub.hpp"

#include "Log.hpp"




struct	MemoryBusInterfaceSettings : public Settings
{
	std::string	memoryName ;
	std::string	busName ;
	unsigned	baseAddress ;
	unsigned	size ;
	
	MemoryBusInterfaceSettings ( std::string const & name )
	:	Settings (name)
	,	memoryName ( "" )
	,	busName ( "" )
	,	baseAddress(0)
	,	size(0)
		{
		add ( "memory" , memoryName ) ;
		add ( "bus" , busName ) ;
		add ( "baseAddress" , baseAddress ) ;
		add ( "size" , size ) ;
		}
	
	virtual void validate ()
		{
		if ( memoryName.size() == 0 )
			ThrowException ( "MemoryBusInterfaceSettings" , "Memory name's length is 0" , __FILE__,__LINE__ ) ;
		//Log::error("MemoryBusInterfaceSettings") << "Memory name's length is 0" ;
		if ( busName.size() == 0 )
			ThrowException ( "MemoryBusInterfaceSettings" , "Bus name's length is 0" , __FILE__,__LINE__ ) ;
		//Log::error("MemoryBusInterfaceSettings") << "Bus name's length is 0" ;
		if ( size == 0 )
			ThrowException ( "MemoryBusInterfaceSettings" , "Address name's length is 0" , __FILE__,__LINE__ ) ;
		//Log::error("MemoryBusInterfaceSettings") << "Address length is 0" ;
		}
	
} ;










struct	BusSlave_MemoryInterface : public ConfiguredModel<MemoryBusInterfaceSettings>
{
	sc_port < Bus::SlaveInterface >		bus ;
	sc_port < simple_mem_if >			memory ;
	
	
	
	SC_HAS_PROCESS(BusSlave_MemoryInterface) ;
	
	BusSlave_MemoryInterface ( ModelVariant const & name )
	:	ConfiguredModel<MemoryBusInterfaceSettings> ( name )
	,	bus			()
	,	memory		()
		{
		Subscription<Bus::SlaveInterface>	_bus		( mConfig.busName ) ;
		bindPortToInterface ( &bus , _bus ) ;
		
		Subscription<simple_mem_if>			_mem		( mConfig.memoryName ) ;
		bindPortToInterface ( &memory , _mem ) ;
		
		
		SC_THREAD ( runloop ) ;
		}
	
	
	void	runloop ()
		{
		Bus::Request	request ;
		
		while ( 1 )
			{
			bus -> waitForNewRequest ( request ) ;
			
			if ( _isRecognized(request) )
				{
				bus -> acknowledgeRequest() ;
				
				if ( request.operation == Bus::kOperationRead )
					_handleRead ( request ) ;
				else
					_handleWrite ( request ) ;
				}
			}
		}
	
	bool	_isRecognized	( Bus::Request & request )
		{
		if ( request.baseAddress >= mConfig.baseAddress )
			{
			unsigned base	=	request.baseAddress - mConfig.baseAddress ;
			if ( base < mConfig.size )
				{
				if ( base+request.blockLength < mConfig.size )
					{
					return true ;
					}
				}
			}
		return false ;
		}
	void	_handleRead		( Bus::Request & request )
		{
		unsigned here	=	request.baseAddress - mConfig.baseAddress ;
		unsigned end	=	here + request.blockLength ;
			
		while ( here < end )
			{
			unsigned const &	addr	=	here++ ;
			Log::states("BusSlave_MemoryInterface") << "Reading * " << addr ;
			unsigned			value	=	0 ;
			memory -> Read ( addr , value ) ;
			bus -> writeWord ( Bus::Word(value) ) ;
			Log::states("BusSlave_MemoryInterface") << "Value = " << value ;
			}
		}
	void	_handleWrite	( Bus::Request & request )
		{
		unsigned here	=	request.baseAddress - mConfig.baseAddress ;
		unsigned end	=	here + request.blockLength ;
		
		while ( here < end )
			{
			unsigned const &	addr	=	here++ ;
			Log::states("BusSlave_MemoryInterface") << "Writing * " << addr ;
			Bus::Word			value ;
			bus -> readWord ( value ) ;
			memory -> Write ( addr , value ) ;
			Log::states("BusSlave_MemoryInterface") << "Value = " << value ;
			}
		}
} ;


TypedModuleMaker<BusSlave_MemoryInterface>	_BusSlave_MemoryInterface ( "busToMemory" ) ;














struct	BusMaster_MemoryInterface : public ConfiguredModel<MemoryBusInterfaceSettings> , public virtual simple_mem_if
{
	sc_port < Bus::MasterInterface >	bus ;
	Publication<simple_mem_if>			thisPublisher ;
	
	
	BusMaster_MemoryInterface	( ModelVariant const & name )
	:	ConfiguredModel<MemoryBusInterfaceSettings> ( name )
	,	bus()
	,	thisPublisher()
		{
		Subscription<Bus::MasterInterface>	_bus		( mConfig.busName ) ;
		bindPortToInterface ( &bus , _bus ) ;
		
		simple_mem_if*	thisInterface	=	this ;
		thisPublisher = mConfig.memoryName ;
		thisPublisher = thisInterface ;
		}
	
	
	
	virtual bool	Write	( unsigned int addr , unsigned int  data )
		{
		Bus::Address sender = Bus::uniqueAddress ( static_cast<void*>(this) ) ;
		
		Bus::Request request ( addr+mConfig.baseAddress , Bus::kOperationWrite ) ;
		bus -> request ( sender , request ) ;
		if ( bus -> waitForAcknowledge ( sender ) )
			{
			bus -> writeWord ( Bus::Word(data) ) ;
			return true ;
			}
		return false ;
		}
    virtual bool	Read	( unsigned int addr , unsigned int& data )
		{
		Bus::Address sender = Bus::uniqueAddress ( static_cast<void*>(this) ) ;
		
		Bus::Request request ( addr+mConfig.baseAddress , Bus::kOperationRead ) ;
		bus -> request ( sender , request ) ;
		if ( bus -> waitForAcknowledge ( sender ) )
			{
			Bus::Word w ;
			bus -> readWord ( w ) ;
			data = w ;
			return true ;
			}
		return false ;
		}
} ;


TypedModuleMaker<BusMaster_MemoryInterface>	_BusMaster_MemoryInterface ( "memoryToBus" ) ;
